"""
表达式模型
"""
import os
from abc import abstractmethod, ABCMeta

from core.error import CodeDomError


class Expression(metaclass=ABCMeta):
    """
    表达式Expression的基类，
    表达式只需要负责输出表达式的内容即可，
    不需要考虑代码缩进
    """

    @abstractmethod
    def to_code(self):
        """由子类去实现表达式的输出内容"""
        pass


class VariableInvokeExpression(Expression):
    """
    变量引用表达式
    表达式范例：var、str_field
    """

    def __init__(self, var_name):
        self.var_name = var_name

    def to_code(self):
        return self.var_name


class ConstInvokeExpression(Expression):
    """
    常量引用表达式
    表达式范例: 12345、"abc"
    """

    def __init__(self, const_value):
        self.const_value = const_value

    def to_code(self):
        if isinstance(self.const_value, int) or isinstance(self.const_value, float):
            return str(self.const_value)
        elif self.const_value is None:
            return "None"
        else:
            return f"\"{self.const_value}\""


class NoneExpression(ConstInvokeExpression):
    """
    None的表达式
    表达式范例: None
    """

    def __init__(self):
        super().__init__(None)


class TrueExpression(ConstInvokeExpression):
    """
    True的表达式
    表达式范例: True
    """

    def __init__(self):
        super().__init__(True)


class FalseExpression(ConstInvokeExpression):
    """
    False的表达式
    表达式范例: False
    """

    def __init__(self):
        super().__init__(False)


class AssignExpression(Expression):
    """
    赋值表达式
    表达式范例: var = "123"、 is_meet = True
    """

    def __init__(self, left_expression, right_expression):
        if not isinstance(left_expression, Expression):
            raise CodeDomError(f"左边表达式应是一个Expression的子类:{left_expression}")
        if not isinstance(right_expression, Expression):
            raise CodeDomError(f"右边表达式应是一个Expression的子类:{right_expression}")
        self.left_expression = left_expression
        self.right_expression = right_expression

    def to_code(self):
        return f"{self.left_expression.to_code()} = {self.right_expression.to_code()}"


class FieldInvokeExpression(Expression):
    """
    对象属性引用表达式
    表达式范例: Response.text、inst.filed
    """

    def __init__(self, instance_expression, filed_expression):
        if not isinstance(instance_expression, Expression):
            raise CodeDomError(f"实例名称应是一个Expression的子类:{instance_expression}")
        if not isinstance(filed_expression, Expression):
            raise CodeDomError(f"字段属性应是一个Expression的子类:{filed_expression}")
        self.instance_expression = instance_expression
        self.field_expression = filed_expression

    def to_code(self):
        return f"{self.instance_expression.to_code()}.{self.field_expression.to_code()}"


class MethodInvokeExpression(Expression):
    """
    方法引用表达式
    表达式范例:list.pop()、dict()
    """

    def __init__(self, method_name, *args, **kwargs):
        """
        :param method_name: 方法名称
        :param args: 方法的参数，传入每个参数都应为Expression的子类
        :param kwargs: 如果是对象方法引用，需要将对象名传入{"instance": 对象名}
        """
        for arg in args:
            if not isinstance(arg, Expression):
                raise CodeDomError(f"参数应是Expression的子类{arg}")
        self.method_name = method_name
        self.arg_list = list()
        for arg in args:
            self.arg_list.append(arg)
        self.obj = kwargs.get("instance", None)

    def to_code(self):
        if self.obj:
            # 对象方法引用表达式：list.pop()
            return f"{self.obj.to_code()}.{self.method_name}({', '.join([x.to_code() for x in self.arg_list])})"
        else:
            # 方法引用表达式：dict()
            return f"{self.method_name}({', '.join([x.to_code() for x in self.arg_list])})"


class InstanceCreationExpression(Expression):
    """
    类对象表达式
    表达式范例: Student()
    """

    def __init__(self, class_name, *args):
        """
        :param method_name: 类名称
        :param args: 类的参数，传入每个参数都应为Expression的子类
        """
        for arg in args:
            if not isinstance(arg, Expression):
                raise CodeDomError(f"参数应是Expression的子类{arg}")
        self.class_name = class_name
        self.arg_list = args

    def to_code(self):
        return f"{self.class_name}({', '.join([x.to_code() for x in self.arg_list])})"


class DictInvokeExpression(Expression):
    """
    字典表达式
    表达式范例: key["value"]
    """

    def __init__(self, dict_name, key_expression):
        if not isinstance(key_expression, Expression):
            raise CodeDomError(f"键应是一个Expression的子类:{key_expression}")
        self.dict_name = dict_name
        self.key_name = key_expression

    def to_code(self):
        return f"{self.dict_name}[{self.key_name.to_code()}]"


class ParameterDefineExpression(Expression):
    """
    参数定义表达式
    表达式范例func(param, param2="123")
    """

    def __init__(self, name, default_value=None):
        if default_value is not None and not isinstance(default_value, Expression):
            raise CodeDomError(f"参数默认值应为Expression的子类{default_value}")
        self.name = name
        self.default_value = default_value

    def to_code(self):
        ret = self.name
        if self.default_value is not None:
            return f"{ret}={self.default_value.to_code()}"
        else:
            return ret


class BinaryOperatorExpression(Expression):
    """
    二元运算表达式
    表达式范例： a == b、 1 < 2
    """

    def __init__(self, op_expression1, op_expression2, operator):
        """
        :param op_expression1: 左侧运算表达式
        :param op_expression2: 右侧运算表达式
        :param operator: 运算符
        """
        if not isinstance(op_expression1, Expression):
            raise CodeDomError("expression1 should be an expression")
        if not isinstance(op_expression2, Expression):
            raise CodeDomError("expression2 should be an expression")
        self.expression1 = op_expression1
        self.expression2 = op_expression2
        self.operator = operator

    def to_code(self):
        return f"{self.expression1.to_code()} {self.operator} {self.expression2.to_code()}"


class ListExpression(Expression):
    """
    列表表达式
    表达式范例: [1,2,3,4,5]
    """

    def __init__(self, expression_list):
        self.expression_list = expression_list

    def to_code(self):
        rv_list = ", ".join([exp.to_code() for exp in self.expression_list])
        return f"[{rv_list}]"


class SelfExpression(Expression):
    """
    self表达式
    """

    def to_code(self):
        return "self"


def _get_indent(indent):
    return "    " * indent


class Statement(metaclass=ABCMeta):
    """
    语句基类，需要实现语句输出和缩进
    """

    def to_code(self, indent=0):
        return self._to_code(indent) + os.linesep

    @abstractmethod
    def _to_code(self, indent=0):
        pass


class ExpressionStatement(Statement):
    """
    表达式语句
    """

    def __init__(self, expression):
        if not isinstance(expression, Expression):
            raise CodeDomError(f"{expression}应为Expression的子类")
        self.expression = expression

    def _to_code(self, indent=0):
        return f"{_get_indent(indent)}{self.expression.to_code()}"


class ImportStatement(Statement):
    """
    包引用语句
    语句范例 from random import randint、 import os
    """

    def __init__(self, packages: list, from_package=None):
        """
        :param packages: 需要导入的包名，可以导入多个
        :param from_package: 导包模块路径，按照平常导包的方式填入模块路径即可
        """
        self.packages = packages
        self.from_package = from_package

    def _to_code(self, indent=0):
        ret = f"import {', '.join(self.packages)}"
        if self.from_package:
            ret = f"from {self.from_package}{ret}"
        return ret


class BlankStatement(Statement):
    """空语句"""

    def _to_code(self, indent=0):
        return ""


class MethodDefineStatement(Statement):
    """
    方法定义语句
    """

    def __init__(self, method_name, *args):
        self.method_name = method_name
        self.decorators = list()
        self.args = [arg for arg in args]
        self.doc = None
        self.body = list()

    def _to_code(self, indent=0):
        ret = ""
        for decorator in self.decorators:
            ret += f"{_get_indent(indent)}@{decorator.to_code()}\n"
        ret += f"{_get_indent(indent)}def {self.method_name}({', '.join([x.to_code() for x in self.args])}):\n"
        if self.doc is not None:
            ret += self.doc.to_code(indent+1)
        for segment in self.body:
            ret += segment.to_code(indent+1)
        return ret


class ReturnStatement(Statement):
    """
    return语句
    """
    def __init__(self, expression):
        if not isinstance(expression, Expression):
            raise CodeDomError(f"return出去的表达式:{expression}应为Expression的子类")
        self.expression = expression

    def _to_code(self, indent=0):
        return f"{_get_indent(indent)}return {self.expression.to_code()}"


class DocStatement(Statement):
    def __init__(self, lines):
        self.lines = lines

    def _to_code(self, indent=0):
        rv = f"{_get_indent(indent)}\"\"\"" + "\n"
        for line in self.lines:
            rv += f"{_get_indent(indent)}{line}\n"
        rv += f"{_get_indent(indent)}\"\"\""
        return rv


class IfStatement(Statement):
    def __init__(self, condition):
        self.condtion = condition
        self.true_statements = list()
        self.false_statements = list()

    def _to_code(self, indent=0):
        rv = f"{_get_indent(indent)}if {self.condtion.to_code()}:\n"
        for statement in self.true_statements:
            rv += statement.to_code(indent+1)
        rv += f"{_get_indent(indent)}else:\n"
        for statement in self.false_statements:
            rv += statement.to_code(indent+1)
        return rv


class PassStatement(Statement):

    def _to_code(self, indent=0):
        return f"{_get_indent(indent)}pass"


class ClassDefineStatement(Statement):
    """
    """
    def __init__(self, class_name, parent=None, doc=None):
        self.class_name = class_name
        self.parent = parent
        self.decorators = list()
        self.body = list()
        self.doc = doc

    def _to_code(self, indent=0):
        ret = ""
        for decorator in self.decorators:
            ret += f"{_get_indent(indent)}@{decorator.to_code()}\n"
        ret += f"{_get_indent(indent)}class {self.class_name}"
        if self.parent is not None:
            ret += f"({self.parent})"
        ret += f":\n"
        if self.doc is not None:
            ret += self.doc.to_code(indent+1)
        for segment in self.body:
            ret += segment.to_code(indent+1)
        return ret



if __name__=="__main__":
    mh = MethodDefineStatement("add")
    mh.decorators.append(VariableInvokeExpression("check"))
    mh.args.append(ParameterDefineExpression("a"))
    mh.args.append(ParameterDefineExpression("b"))
    mh.body.append(
        ExpressionStatement(
            AssignExpression(
                VariableInvokeExpression("ret"),
                BinaryOperatorExpression(
                    VariableInvokeExpression("a"),
                    VariableInvokeExpression("b"),
                    "+"))))
    mh.body.append(
        ReturnStatement(
            VariableInvokeExpression("ret")
        )
    )
    with open("./test.py", "w", encoding="utf8") as file:
        file.write(mh.to_code())
    print(mh.to_code())